
use std::fs::File;
use std::io::{self, Write};
use std::process::Command;

use sysinfo::{
    Components, CpuRefreshKind, Disks, Networks, Pid, RefreshKind, System, Users,
};

const POS: &str = "[+]";
const NEG: &str = "-";

fn main() -> io::Result<()> {
    // Create or open the output file
    let mut output_file = File::create("system_info.txt")?;

    // Create a system object and refresh its data
    let mut system = sysinfo::System::new_all();
    system.refresh_all();

    // Redirect output to the file using a closure
    let mut log = |msg: &str| {
        writeln!(output_file, "{}", msg).expect("Failed to write to file");
    };

    log(&format!("{} System Information", POS));
    log(&format!(
        "{} OS: {} {}",
        NEG,
        System::name().unwrap_or("Unable to find system name".to_string()),
        System::os_version().unwrap_or("Unknown".to_string())
    ));
    log(&format!(
        "{} Host name: {}",
        NEG,
        System::host_name().unwrap_or("Unable to find Host Name".to_string())
    ));
    log("");

    log(&format!("{} CPU Information:", POS));
    let s = System::new_with_specifics(RefreshKind::new().with_cpu(CpuRefreshKind::everything()));

    log(&format!("{} CPU Usage: {}%", NEG, s.global_cpu_usage()));

    let cpu_info = system.global_cpu_usage();
    log(&format!("{} Model: {:?}", NEG, cpu_info));
    log(&format!("{} Usage: {}", NEG, cpu_info));
    let cpu_cores = system.physical_core_count();
    if let Some(cores) = cpu_cores {
        log(&format!("{} Cores: {}", NEG, cores));
    } else {
        log(&format!("{} Unable to Retrieve CPU cores!", NEG));
    }
    log("");

    log(&format!("{} Disk Information", POS));
    for disk in system {
        log(&format!(
            "{} Name: {} FS: {:?} Removable: {} Mount Point: {:?} Disk Space: {:.2} GB / {:.2} GB",
            NEG,
            disk.name().to_string_lossy(),
            disk.file_system(),
            disk.is_removable(),
            disk.mount_point(),
            disk.available_space() as f64 / (1024.0 * 1024.0 * 1024.0),
            disk.total_space() as f64 / (1024.0 * 1024.0 * 1024.0)
        ));
    }
    log("");

    log(&format!("{} User Information", POS));
    for user in system.users() {
        log(&format!("{} User: {} : {:?}", NEG, user.name(), user.name()));
    }
    log("");

    log(&format!("{} Network Adapters", POS));
    for (interface_name, _) in system.networks() {
        log(&format!("{} {}B", NEG, interface_name));
    }
    log("");

    log(&format!("{} Custom PID's", POS));
    let check_process_lists = ["explorer.exe", "winlogon.exe", "wininit.exe"];
    for process in check_process_lists {
        if let Some(proc) = system.processes_by_name(process).next() {
            let pid = proc.pid().as_u32();
            log(&format!("{} PID: {} : {}", NEG, process, pid));
        } else {
            log(&format!("{} No such process: {}", NEG, process));
        }
    }
    log("");

    log(&format!("{} ARP Table", POS));
    let arp = Command::new("powershell.exe")
        .args(&["arp", "-a"])
        .output()
        .expect("Failed to execute command");

    if arp.status.success() {
        log(&format!("{} Interface IPs: {}", NEG, String::from_utf8_lossy(&arp.stdout)));
    } else {
        log(&format!(
            "Command not found. Exit status: {}",
            arp.status.code().unwrap_or_default()
        ));
    }
    log("");

    log(&format!("{} PID Termination", POS));
    if let Some(proc) = system.processes_by_name("notepad.exe").next() {
        let pid_demo = proc.pid();
        if let Some(proc_to_kill) = system.process(pid_demo) {
            proc_to_kill.kill();
            log(&format!("{} PID {} Terminated Successfully", NEG, pid_demo));
        } else {
            log(&format!("{} PID Not Found!", NEG));
        }
    } else {
        log(&format!("{} No Such Process: notepad.exe", NEG));
    }

    Ok(())
}
